#ifndef _CANARY2_HTTP_MESSAGE_DETAILS_H_
#define _CANARY2_HTTP_MESSAGE_DETAILS_H_

#include <string_view>
#include <map>
#include "version.h"
#include "status.h"
#include "method.h"
#include <memory>
#include "chunked_buffer.h"
#include <variant>
#include <vector>
#include "../sys/fd_guard.h"
#include <filesystem>

#define CRLF "\r\n"
#define CRLF_SV ::std::string_view{ CRLF }

namespace wilson::http::details
{
    template<typename StringLike>
    struct message_object
    {
        using string_type = StringLike;
        http::version   m_version{version::HTTP_1_1};
        http::status    m_status{};
        http::method    m_method{};
        ::std::variant<string_type, ::std::filesystem::path> m_body;
        string_type     m_url;
        string_type     m_path;
        string_type     m_query;
        string_type     m_fragment;
        bool            m_keep_alive{};
        bool            m_web_socket{};
        ::std::multimap<string_type, string_type> m_headers;
    };

    class message_from_net : public message_object<::std::string_view>
    {
    public:
        using storage_type = http::chunked_buffer;

    public:
        message_from_net() = default;
        message_from_net(storage_type&& stor) : message_from_net(::std::make_shared<storage_type>(::std::move(stor))) {}
        message_from_net(::std::shared_ptr<storage_type> stor) noexcept : m_storage{ ::std::move(stor) } {}
        void set_storage(storage_type&& stor) { ::std::make_shared<storage_type>(::std::move(stor)); }
        void set_storage(::std::shared_ptr<storage_type> stor) noexcept
            { m_storage = stor; }
        auto storage_pointer() noexcept { return m_storage; }
        auto& storage() noexcept { return *m_storage; }
        ::std::span<const ::std::byte> to_payload() const;
        ::std::string_view to_payload_string() const;

    private:
        ::std::shared_ptr<storage_type> m_storage{};
    };

    class message_to_net : public message_object<::std::string>
    {
    public:
        using storage_type = http::chunked_buffer;
        
    public:
        void fill_in_storage() const;
        auto storage_pointer() noexcept { return m_storage; }
        auto& storage() noexcept { return *m_storage; }
        ::std::span<const ::std::byte> to_payload() const;
        ::std::string_view to_payload_string() const;
        bool has_attached_file() const noexcept { return m_body.index() == 1; }
        ::std::pair<bool, ::std::filesystem::path> attached_file();
        
    private:
        mutable bool m_filled{ false };
        sys::fd_guard m_attached_file;
        ::std::shared_ptr<storage_type> m_storage{ 
            ::std::make_shared<storage_type>() 
        };
    };
}

#endif
